/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "core/common/recorder/event_controller.h"

#include <algorithm>

#include "base/json/json_util.h"
#include "base/log/log_wrapper.h"
#include "base/thread/background_task_executor.h"
#include "core/common/recorder/node_data_cache.h"

namespace OHOS::Ace::Recorder {
EventController& EventController::Get()
{
    static EventController eventController;
    return eventController;
}

void EventController::Register(std::string* config, UIEventObserver* observer)
{
    LOGI("UIEvent Register config");
    LOGD("UIEvent Register config %{public}s", config->c_str());
    if (!observer) {
        return;
    }
    bool change = false;
    UIEventClient client;
    if (config) {
        client.config.Init(*config);
        change = true;
    }
    if (!client.config.IsEnable()) {
        return;
    }
    client.ob = observer;
    std::unique_lock<std::mutex> lock(cacheLock_);
    clientList_.emplace_back(std::move(client));
    if (change) {
        NotifyConfigChange();
    }
}

void EventController::NotifyConfigChange()
{
    auto mc = std::make_shared<MergedConfig>();
    for (auto&& client : clientList_) {
        if (!client.config.IsEnable()) {
            continue;
        }
        for (auto iter = client.config.GetConfig()->begin(); iter != client.config.GetConfig()->end(); iter++) {
            auto mcNodeIt = mc->shareNodes.find(iter->first);
            if (mcNodeIt != mc->shareNodes.end()) {
                std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
                    [&mcNodeIt](const std::list<std::string>::value_type& id) { mcNodeIt->second.emplace(id); });
            } else {
                std::set<std::string> nodeSet;
                std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
                    [&nodeSet](const std::list<std::string>::value_type& id) { nodeSet.emplace(id); });
                mc->shareNodes.emplace(iter->first, std::move(nodeSet));
            }

            auto mcExposureIt = mc->exposureNodes.find(iter->first);
            if (mcExposureIt != mc->exposureNodes.end()) {
                std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
                    [&mcExposureIt](
                        const std::list<ExposureCfg>::value_type& cfg) { mcExposureIt->second.emplace(cfg); });
            } else {
                std::set<ExposureCfg> exposureSet;
                std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
                    [&exposureSet](const std::list<ExposureCfg>::value_type& cfg) { exposureSet.emplace(cfg); });
                mc->exposureNodes.emplace(iter->first, std::move(exposureSet));
            }
        }
    }
    NodeDataCache::Get().UpdateConfig(std::move(mc));
}

void EventController::Unregister(UIEventObserver* observer)
{
    bool change = false;
    std::unique_lock<std::mutex> lock(cacheLock_);
    if (observer) {
        auto iter = std::remove_if(clientList_.begin(), clientList_.end(),
            [&observer](UIEventClient client) { return client.ob == observer; });
        clientList_.erase(iter, clientList_.end());
        change = iter != clientList_.end();
    }
    if (change) {
        NotifyConfigChange();
    }
}

void EventController::GetNodeProperty(
    std::string* pageUrl, std::unordered_map<std::string, std::string>* nodeProperties)
{
    if (!pageUrl || !nodeProperties) {
        return;
    }
    NodeDataCache::Get().GetNodeData(*pageUrl, *nodeProperties);
}

void EventController::NotifyEvent(EventCategory category, int32_t eventType,
    const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
{
    BackgroundTaskExecutor::GetInstance().PostTask([this, category, eventType, eventParams]() {
        std::unique_lock<std::mutex> lock(cacheLock_);
        for (auto&& client : this->clientList_) {
            if (client.config.IsEnable() && client.config.IsCategoryEnable(category)) {
                client.ob->NotifyUIEvent(eventType, *eventParams);
            }
        }
    });
}
} // namespace OHOS::Ace::Recorder
